import os
import random
import time

from PyQt5.QtCore import Qt
from PyQt5.QtGui import QCursor
from PyQt5.QtWidgets import QPushButton, QSplitter, QAction, QTreeWidgetItem, QMenu, QMessageBox, QTreeWidget, \
    QScrollBar, QLabel, QComboBox, QPlainTextEdit
from PyQt5.uic import loadUi

from base_api.base import PygameMixerTaskClass
from base_api.base.MessageClass import Message
from base_api.base.PygameMixerTaskClass import PygameMixerTask
from base_api.base.VideoInfoClass import VideoInfo
from pyqt_api import TreeWidgetModule
from pyqt_api.PyqtThreadPoolClass import PyqtThreadPool

"""
音频播放子窗口
"""


class PlayWindow:
    def __init__(self, home_window):
        super().__init__()
        self.window = loadUi('ui/play.ui')
        self.home_window = home_window

        # 待播放音频文件的路径
        self.this_video_path = ''
        # 待播放音频文件的信息
        self.this_video_info = None
        """
        两个时间节点，float类型，单位秒
        1表示当前音频的开始节点，2表示当前音频的实时播放节点。
        2有三种场景
        (1)如果在播放过程中，2由1和播放器实时反馈的播放时长决定，进而设置进度条的显示。
        (2)如果在播放停止时，拖拽进度条，会根据判断是人为操作影响，对2基于进度条进行赋值。
        (3)点击树组件或者上一首、下一首，切换音频时，1和2都置为0
        在决定播放音频时，会根据2设置1
        """
        self.this_video_point_1 = 0.0
        self.this_video_point_2 = 0.0

        # 待播放音频的同目录音频队列
        self.video_path_list = list()
        # 当前播放音频在队列中的索引
        self.video_path_list_current = 0

        # 水平分裂器 比例设置
        self.window.splitter: QSplitter
        self.window.splitter.setSizes([100, 400])

        """树组件"""
        self.window.treeWidget.header().hide()
        self.window.treeWidget.setColumnCount(1)
        # 树组件刷新显示
        self.treeWidget_refresh()
        # 右键菜单
        self.window.treeWidget.setContextMenuPolicy(Qt.CustomContextMenu)
        self.window.treeWidget.customContextMenuRequested.connect(self.treeWidget_rightmenu)
        # 双击节点 如果是文件则进行选中和播放
        self.window.treeWidget.itemDoubleClicked.connect(self.treeWidget_itemDoubleClicked)

        # 进度条组件
        self.window.horizontalScrollBar: QScrollBar
        self.window.horizontalScrollBar.setMinimum(0)
        self.window.horizontalScrollBar.setMaximum(100)
        self.window.horizontalScrollBar.setValue(0.0)
        self.window.horizontalScrollBar.valueChanged.connect(self.horizontalScrollBar_valueChanged)
        # 播放模式 下拉列表
        self.window.comboBox: QComboBox
        self.window.comboBox.clear()
        self.window.comboBox.addItems(['单曲播放', '单曲循环', '顺序播放', '循环播放', '随机播放'])

        # 上一首
        self.window.pushButton: QPushButton
        self.window.pushButton.clicked.connect(self.pre_video)
        # 播放控制按钮
        self.window.pushButton_2: QPushButton
        self.window.pushButton_2.clicked.connect(self.control)
        # 下一首
        self.window.pushButton_3: QPushButton
        self.window.pushButton_3.clicked.connect(self.next_video)

        # 播放音频任务对象
        self.pygameMixerTask = PygameMixerTask()
        # 线程对象
        self.pyqtThreadPool = None
        # 工作状态，0表示未开始/结束，1表示运行中
        self.work_status = 0

    """点击上一首按钮"""

    def pre_video(self):
        self.pre_or_next(True)

    """点击下一首按钮"""

    def next_video(self):
        self.pre_or_next(False)

    def pre_or_next(self, flag: bool):
        # 必须选择有当前音频
        if self.this_video_path == '':
            QMessageBox.warning(self.window, '警告', '当前音频还未选择')
            return
        # 如果当前有音频正在播放，需要暂停。相当于点击一次播放/暂停按钮
        if self.work_status == 1:
            self.control()
        # 适当间隔
        time.sleep(1)
        PygameMixerTaskClass.set_isBusy_flag(False)
        # 根据播放模式执行 '单曲播放', '单曲循环', '顺序播放', '循环播放', '随机播放'
        self.window.comboBox: QComboBox
        index = self.window.comboBox.currentIndex()
        if index == 4:
            self.video_path_list_current = random.randint(0, len(self.video_path_list))
        else:
            # 上一首
            if flag:
                self.video_path_list_current = self.video_path_list_current - 1
                if self.video_path_list_current == -1:
                    self.video_path_list_current = len(self.video_path_list) - 1
            # 下一首
            else:
                self.video_path_list_current = self.video_path_list_current + 1
                if self.video_path_list_current == len(self.video_path_list):
                    self.video_path_list_current = 0
        self.set_this_video(path=self.video_path_list[self.video_path_list_current], flag=False)
        # 切换后，播放
        self.control()

    def treeWidget_refresh(self):
        self.window.treeWidget: QTreeWidget
        # 清除
        self.window.treeWidget.clear()
        # 以树组件为父节点，添加当前工作目录下的所有目录，以及.mp3、.wav等音频文件。保留空目录
        TreeWidgetModule.treeWidget_addFile(treeWidget=self.window.treeWidget,
                                            dir_path=self.home_window.this_workspace_dir,
                                            file_endWith_list=['.mp3', '.wav'])
        # 以树组件为父节点，添加com公共目录作为顶级节点，加载其下的子目录和音频文件
        com = QTreeWidgetItem(['com'])
        self.window.treeWidget.addTopLevelItem(com)
        TreeWidgetModule.treeWidgetItem_addFile(treeWidgetItem=com, dir_path='data/com',
                                                file_endWith_list=['.mp3', '.wav'])

    def treeWidget_rightmenu(self, pos):
        """ 查看当前选中项，如果是文件则进行文件操作，反之则进行目录相关操作"""
        self.window.treeWidget: QTreeWidget
        # 获取当前节点
        current_item = self.window.treeWidget.currentItem()
        if current_item is not None:
            # 获取其节点和父节点
            item_list = TreeWidgetModule.treeWidgetItem_where(treeWidgetItem=current_item)
            # 根据节点进行拼接, 获取节点路径
            path = TreeWidgetModule.treeWidgetItem_list_toPath(item_list)
            # 如果顶级节点是com
            if item_list[0].text(0) == 'com':
                path = 'data/' + path
            # 否则其顶级节点是在当前工作目录下
            else:
                path = self.home_window.this_workspace_dir + '/' + path
            # 判断节点是否存在
            if not os.path.exists(path):
                QMessageBox.warning(self.window, '警告', '选中节点的路径不存在，需要检查或重新加载')
            else:
                menu = QMenu(self.window.treeWidget)
                menu.addAction('刷新')
                menu.triggered.connect(self.treeWidget_rightmenu_triggered)
                # 将菜单显示在鼠标位置
                menu.exec_(QCursor.pos())

    def treeWidget_rightmenu_triggered(self, act: QAction):
        if act.text() == '刷新':
            self.treeWidget_refresh()

    def treeWidget_itemDoubleClicked(self, item: QTreeWidgetItem, column: int):
        # 获取其节点和父节点
        item_list = TreeWidgetModule.treeWidgetItem_where(treeWidgetItem=item)
        # 根据节点进行拼接, 获取节点路径
        path = TreeWidgetModule.treeWidgetItem_list_toPath(item_list)
        # 如果顶级节点是com
        if item_list[0].text(0) == 'com':
            path = 'data/' + path
        # 否则其顶级节点是在当前工作目录下
        else:
            path = self.home_window.this_workspace_dir + '/' + path
        # 判断节点是否为文件
        if os.path.exists(path) and os.path.isfile(path):
            self.set_this_video(path)

    def horizontalScrollBar_valueChanged(self, value):
        # 未执行状态, 则进度条变化是人为拖拽或者初始归零设置
        if self.work_status == 0:
            if value == 0:
                self.this_video_point_2 = 0.0
            else:
                # 基于value计算时间，单位秒
                self.window.horizontalScrollBar: QScrollBar
                self.this_video_point_2 = value/self.window.horizontalScrollBar.maximum()*self.this_video_info.length_sec
                # 保留两位小数
                self.this_video_point_2 = int(self.this_video_point_2*100)/100
            # 同步拖拽或归零操作到标签，显示单位为秒
            self.window.label: QLabel
            self.window.label.setText(str(self.this_video_point_2))

    def set_this_video(self, path: str, flag=True):
        self.this_video_path = path
        # 解析信息
        self.this_video_info = VideoInfo(self.this_video_path)
        # 输出音频信息
        self.window.plainTextEdit: QPlainTextEdit
        self.window.plainTextEdit.setPlainText(self.this_video_info.toString())
        # 归位0
        self.window.horizontalScrollBar: QScrollBar
        self.window.horizontalScrollBar.setValue(0)

        # 默认为True，表示需要初始化音频队列，一般是树组件选择时发生。而基于播放控制，上一曲、下一曲操作时，不进行队列初始化以节约资源
        if flag:
            # 初始化音频队列
            self.video_path_list.clear()
            name_list = os.listdir(self.this_video_info.dirPath)
            for i in range(len(name_list)):
                path = self.this_video_info.dirPath + name_list[i]
                if os.path.isfile(path) and '.' in path:
                    if path[path.index('.'):] in ['.mp3', '.wav']:
                        self.video_path_list.append(path)
            # 计算当前音频在音频的索引
            self.video_path_list_current = self.video_path_list.index(self.this_video_path)

    def control(self):
        # 未开始
        if self.work_status == 0:
            # 如果没有选中项，警告
            if self.this_video_path == '' or not os.path.exists(self.this_video_path):
                QMessageBox.warning(self.window, '警告', '未选中任何音频或者选中音频不存在')
                return
            # 当前有其它子窗口的播放器对象在工作
            if not PygameMixerTaskClass.get_chance():
                QMessageBox.warning(self.window, '警告', '当前有其它音频正在播放，请手动关闭或等待')
                return
                # 待变更为直接关闭其它子窗口的工作，然后执行当前子窗口的播放任务
            """拿到音频播放标志"""
            # 线程对象创建
            self.pyqtThreadPool = PyqtThreadPool()
            # 线程对象的消息位初始化
            self.pyqtThreadPool.get_signal()
            if self.pyqtThreadPool.signal_this is None:
                QMessageBox.warning(self.window, '警告', '线程池繁忙，请稍后尝试或者手动关闭某线程工作')
                # 用不上就释放
                PygameMixerTaskClass.set_isBusy_flag(False)
            else:
                # 进行相关检查和赋值
                flag = self.init_pygameMixerTask()
                if flag:
                    # 切换到工作状态
                    self.work_status = 1
                    # 变更按钮文本
                    self.window.pushButton_2: QPushButton
                    self.window.pushButton_2.setText('播放中(点击暂停)')
                    # 任务对象的执行方法赋值给线程对象的属性
                    self.pyqtThreadPool.run_task = self.pygameMixerTask.exploit
                    # 绑定消息位
                    self.pyqtThreadPool.signal_this.connect(self.output)
                    # 绑定消息位
                    self.pygameMixerTask.output_access = self.pyqtThreadPool.output
                    # 开启线程
                    self.pyqtThreadPool.start()
                else:
                    # 用不上就释放
                    PygameMixerTaskClass.set_isBusy_flag(False)
        # 正在运行中
        else:
            self.work_status = 0
            self.pygameMixerTask.stop()
            self.window.pushButton_2.setText('播放')

    def output(self, msg: Message):
        print(msg.status, msg.data)
        # 任务执行过程中出现异常
        if msg.status == -3:
            self.work_status = 0
            QMessageBox.warning(self.window, '警告', f'任务执行过程中出现异常，已结束。{msg.data}')
            PygameMixerTaskClass.set_isBusy_flag(False)
        # 消息类结束执行。在测试功能中，不会再开子线程，意味着其表示本次任务的完成
        elif msg.status == -1:
            self.work_status = 0
            PygameMixerTaskClass.set_isBusy_flag(False)
            self.window.pushButton_2: QPushButton
            self.window.pushButton_2.setText('播放')

            # 会进行播放模式判断，以是否继续播放
            self.window.comboBox: QComboBox
            index = self.window.comboBox.currentIndex()
            # 单曲播放，播放完毕后不处理
            # 顺序播放，播放到最后一曲也不处理
            if index == 0 or (index == 2 and self.video_path_list_current == len(self.video_path_list) - 1):
                pass
            # 单曲循环，播放完毕后直接继续播放当前音频
            elif index == 1:
                self.control()
            # 其它请求均相当于点击下一曲
            else:
                self.next_video()

        # 音频播放进度反馈
        elif msg.status == 1:
            self.window.horizontalScrollBar: QScrollBar
            self.window.label: QLabel
            # 为-1表示音频播放完成，直接赋值为音频的最大长度
            if msg.data == -1:
                self.this_video_point_2 = int(self.this_video_info.length_sec*100)/100
                temp = self.window.horizontalScrollBar.maximum()
            # 中间过程，传递的是播放时长，单位毫秒
            else:
                # 计算音频当前播放节点
                self.this_video_point_2 = self.this_video_point_1 + msg.data/1000
                temp = int(self.this_video_point_2/self.this_video_info.length_sec *
                           self.window.horizontalScrollBar.maximum())
            # 标签标记进度，单位秒
            self.window.label.setText(str(int(self.this_video_point_2*100)/100))
            # 进度条设置
            self.window.horizontalScrollBar.setValue(temp)

    def init_pygameMixerTask(self):
        # 如果当前音频文件为空，或者被删除了不存在，返回False
        if self.this_video_path == '' or not os.path.exists(self.this_video_path):
            return False
        # 初始化待播放音频的文件路径
        self.pygameMixerTask.this_video_path = self.this_video_path
        """ 初始化待播放音频的开始时间 """
        self.window.horizontalScrollBar: QScrollBar
        # 如果此时是末尾，会自动切换为0
        if self.window.horizontalScrollBar.value() == self.window.horizontalScrollBar.maximum():
            self.window.horizontalScrollBar.setValue(0)
            self.this_video_point_1 = 0.0
            self.this_video_point_2 = 0.0
            self.window.label.setText('0.0')
        self.this_video_point_1 = self.this_video_point_2
        self.pygameMixerTask.this_start_point = self.this_video_point_1
        return True

